﻿using System;

namespace Generator
{
    public static class EngineAddRemoveShifts
    {
        internal static void TakeShiftOff(
            Solution pSolution,
            int pShiftID,
            int pEmpID)
        {
            pSolution.shift_emp[pShiftID, pEmpID] = true;
            pSolution.OrphanedShiftsLeft += 1;

            pSolution.Shifts[pShiftID].Chosen = -1;
            pSolution.Shifts[pShiftID].CandidatesLeft += 1;
            pSolution.Shifts[pShiftID].IsTaken = false;

            pSolution.Candidates[pEmpID].Hours -= pSolution.Shifts[pShiftID].length;

            var day = pSolution.Shifts[pShiftID].Day;
            var hasOneShiftLeft = false;
            
            var opening = pSolution.Shifts[pShiftID].FromTo.Opening;
            var closing = pSolution.Shifts[pShiftID].FromTo.Closing;
            if (opening)
            {
                pSolution.openers[day, pEmpID] = false;
            }
            if (closing)
            {
                pSolution.closers[day, pEmpID] = false;
            }
            if (opening && closing)
            {
                pSolution.Candidates[pEmpID].FullDays[day] = false;
            }

            if (pSolution.Candidates[pEmpID].Revisits[day]) // it was one of two shifts that day            
            {
                pSolution.Candidates[pEmpID].Revisits[day] = false;
                hasOneShiftLeft = true;
            }
            else
            {
                if (pSolution.Candidates[pEmpID].Days[day])
                // (it *must* be true actually - but, just in case if there's some bug)               
                {
                    pSolution.Candidates[pEmpID].Days[day] = false;
                }
                else
                {
                    throw new ApplicationException("takeShiftOff - day is 0 already"); // oops bug
                }
            }

            #region "freeing" overlapping shifts.
            /* This part of the code "informs" the overlapping shifts that candidate Eric
             * does not work shift pShiftID anymore, so now they can consider him as a candidate
             * to take them up again (while it wasn't possible before that moment).
             * 
             * But there is a little catch here.
             * Imagine there are three shifts during the day: 8 - 14, 12 - 18 and 15 - 20.
             * Our employee worked 8 - 14, and therefore could not be counted on to potentially take up 12 - 18.
             * But now his 8-14 shift is being cancelled: he won't have to do it.
             * Does it mean that we can already happily "tell" the 12-18 shift he's now free to take it up??
             * Well: no we can't... not straight away.
             * Why?
             * Because first we need to make sure he doesn't work 15-20!!!
             * 
             * Note: if shift A overlaps with B, but doesn't overlap with C,
             * it doesn't make impossible for shift B to overlap with C. */
            foreach (int overlapping in pSolution.Shifts[pShiftID].MyOverlaps)
            {
                bool canBeFreed = true;

                if (hasOneShiftLeft)
                {                    
                    // that's what I'm talking about above. we're looking for those sneaky "15-20"s here
                    foreach (var overlappingWithOverlapped in pSolution.Shifts[overlapping].MyOverlaps)
                    {                        
                        if (!DoesWorkAnyOverlappingShift(
                            pSolution,
                            pEmpID,
                            overlappingWithOverlapped,
                            overlapping))
                        {
                            continue;
                        }
                        canBeFreed = false;                        
                        goto Exit; /* I believe in the validity of using "goto" more
                                    * than I believe someone will actually read this snippet :)
                                    * If goto is so bad, then why is it still supported?? */
                    }
                }

            Exit:
                if (canBeFreed)
                {
                    pSolution.Shifts[overlapping].CandidatesLeft += 1;
                    pSolution.shift_emp[overlapping, pEmpID] = true;                    
                }
            }
            #endregion

        }


        internal static void putShiftOn(Solution pSolution, int pShiftID, int pEmployeeID)
        {            
            pSolution[pShiftID, pEmployeeID] = false;
            pSolution.Shifts[pShiftID].Chosen = pEmployeeID;
            pSolution.Shifts[pShiftID].CandidatesLeft -= 1;
            pSolution.Shifts[pShiftID].IsTaken = true;
            var day = pSolution.Shifts[pShiftID].Day;
            if (pSolution.Candidates[pEmployeeID].Days[day])
            {
                pSolution.Candidates[pEmployeeID].Revisits[day] = true;
            }
            else
            {
                pSolution.Candidates[pEmployeeID].Days[day] = true;
            }
            if (pSolution.Shifts[pShiftID].FullShift)
            {
                pSolution.Candidates[pEmployeeID].FullDays[day] = true;
            }
            pSolution.Candidates[pEmployeeID].Hours += pSolution.Shifts[pShiftID].length;
            foreach (var overlap in pSolution.Shifts[pShiftID].MyOverlaps)
            {
                if (pSolution[overlap, pEmployeeID])
                {
                    pSolution[overlap, pEmployeeID] = false;
                    pSolution.Shifts[overlap].CandidatesLeft -= 1;
                }
            }
            #region openers / closers
            if (pSolution.parameters.KeyFlowOptimizationOn &&
                (pSolution.parameters.AssistantsConsideredForKeyFlow || pSolution.Candidates[pEmployeeID].IsStoreRunner))
            //^ keyflow necessary PLUS, either assistants is allowed to carry the key - or the man is a store runner
            {
                bool opening = pSolution.Shifts[pShiftID].FromTo.Opening;
                bool closing = pSolution.Shifts[pShiftID].FromTo.Closing;

                if (opening)
                {
                    pSolution.openers[day, pEmployeeID] = true;
                }
                if (closing &&
                    day < 6) // nobody cares who closes on last Sunday
                {
                    pSolution.closers[day, pEmployeeID] = true;
                }
            }
            #endregion

        }


        /// <summary>
        /// 
        /// </summary>        
        /// <param Name="who">id of the _employee to take the Shift</param>
        /// <param Name="whatShift">id of the Shift to be IsTaken</param>
        /// <param Name="shiftToDrop">which Shift does "who" quit (as in swapping Shifts)? pass -1 if None</param>
        /// <returns>can _employee #who work #whatShift?</returns>
        internal static bool DoesWorkAnyOverlappingShift(
            Solution pSolution,
            int pEmployeeID,
            int pShiftID,
            int pShiftToDrop)
        {
            var shift = pSolution.Shifts[pShiftID];
            foreach (var overlap in shift.MyOverlaps)
            {
                if (overlap != pShiftToDrop &&
                    pSolution.Shifts[overlap].Chosen == pEmployeeID)
                {
                    return true;
                }
            }
            return false;
        }
    }
}
